#pragma once
#include <vector>
#include <string>
#include <string.h>
#include <assert.h>
#include <iostream>
#include <fstream>
#include <algorithm>

#define BUFFER_DEFAULT_SIZE 1024

enum Move
{
    ReadOnly,
    ReadAndMove
};

class Buffer
{
private:
    char* ReadPos() { return &_buffer[_read_index]; }
    const char* ReadPos() const { return &_buffer[_read_index]; }
    char* WritePos() { return &_buffer[_write_index]; }
    const char* WritePos() const { return &_buffer[_write_index]; }
    char* BeginPos() { return &_buffer[0]; }
    const char* BeginPos() const { return &_buffer[0]; }

    // 寻找换行符所在的地址，不存在则返回空指针
    const char* FindCRLF() const
    {
        const void* res = memchr(ReadPos(), '\n', RearReadAbleSize());
        if(res == nullptr)
            res = memchr(BeginPos(), '\n', PrevReadAbleSize());
        return (const char*)res;
    }

public:
    Buffer(uint64_t size = BUFFER_DEFAULT_SIZE) : _buffer(size){}

    Buffer(const void* data, uint64_t len, uint64_t size = BUFFER_DEFAULT_SIZE) : _buffer(size)
    {
        size = std::max(size, len);
        Write(data, len);
    }

    uint64_t GetSize() const { return _buffer.size(); }

    //获取已写未读的数据空间大小
    uint64_t PrevReadAbleSize() const
    {
        if(_read_index <= _write_index)
            return 0;
        else
            return _write_index;
    }

    uint64_t RearReadAbleSize() const
    {
        if(_read_index <= _write_index)
            return _write_index - _read_index;
        else
            return GetSize() - _read_index;
    }

    uint64_t ReadAbleSize() const {return PrevReadAbleSize() + RearReadAbleSize(); }

    // 获取已读且未被覆写的数据或未被写入（空闲区域）的空间大小
    // 为了避免_write_index和_read_index在同一位置引发歧义（可能是全部写入，可能是全部已读），预留_read_index前的空位不写
    uint64_t PrevFreeSize() const
    {
        if(_read_index <= _write_index)
            return _read_index - 1;
        else
            return -1;
    }

    uint64_t RearFreeSize() const
    {
        if(_read_index <= _write_index)
            return GetSize() - _write_index;
        else
            return _read_index - _write_index;
    }

    uint64_t FreeSize() const { return PrevFreeSize() + RearFreeSize(); }
    
    void MoveReadIndex(uint64_t len = -1)
    {
        assert(len <= ReadAbleSize());
        if(len == -1) len = ReadAbleSize();
        _read_index = (_read_index + len) % GetSize();
    }

    void MoveWriteIndex(uint64_t len)
    {
        assert(len <= FreeSize());
        _write_index = (_write_index + len) % GetSize();
    }

    // 判断空闲空间是否能全部写入新数据，不能则扩容
    void EnsureWriteSpace(uint64_t len)
    {
        if(len > FreeSize())
        {
            int cur_size = GetSize();
            int need_size = cur_size + len - FreeSize();
            _buffer.resize(need_size <= 1.5*cur_size ? 1.5*cur_size : need_size);
        }
    }

    uint64_t Read(void* buf, uint64_t len = -1, Move move = ReadAndMove)
    {
        assert(len <= ReadAbleSize());
        if(len == -1) len = ReadAbleSize();
        if(len == 0) return len;

        char* b = (char*) buf; 
        const char* bp = BeginPos(), *rp = ReadPos();
        // read_index后的空间足够读取，则全部在其中读取
        // 否则在读完read_index后的空间后，继续读取read_index前的空间
        int rear_size = RearReadAbleSize();
        if(len <= rear_size) 
        {
            std::copy(rp, rp+len, b);
        }        
        else
        {
            std::copy(rp, rp + rear_size, b);
            std::copy(bp, bp + len -rear_size, b + rear_size);
        }

        if(move == ReadAndMove) MoveReadIndex(len);
        return len;
    }

    uint64_t Read(void* buf, uint64_t len = -1) const
    {
        assert(len <= ReadAbleSize());
        if(len == -1) len = ReadAbleSize();
        if(len == 0) return len;

        char* b = (char*) buf;
        const char* rp = ReadPos(), *bp = BeginPos();
        int rear_size = RearReadAbleSize();
        if(len <= rear_size)
        {
            std::copy(rp, rp+len, b);
        }        
        else
        {
            std::copy(rp, rp + rear_size, b);
            std::copy(bp, bp + len -rear_size, b + rear_size);
        }
        return len;
    }

    uint64_t Read(std::string* buf, uint64_t len = -1, Move move = ReadAndMove)
    {
        assert(len <= ReadAbleSize());
        if(len == -1) len = ReadAbleSize();
        if(len == 0) return len;

        buf->clear();
        buf->resize(len);
        return Read(&(*buf)[0], len, move);
    }

    uint64_t Read(std::string* buf, uint64_t len = -1) const
    {
        assert(len <= ReadAbleSize());
        if(len == -1) len = ReadAbleSize();
        if(len == 0) return len;

        buf->clear();
        buf->resize(len);
        return Read(&(*buf)[0], len);
    }

    friend std::ostream& operator<<(std::ostream& _cout, const Buffer& buf); 

    // 获取一行数据（以换行符为界限）
    bool GetLine(std::string* line, Move move = ReadAndMove)
    {
        const char* pos = FindCRLF();
        if(pos == nullptr)
            return false;

        // 当换行符在 read_index 后的数据中，则长度直接相减，否则先获取 read_index 后的长度，再获取 read_index 前的换行符前的位置
        int len = 0;
        if(pos >= ReadPos())
            len = pos-ReadPos()+1;
        else
            len = RearReadAbleSize() + pos - BeginPos() + 1; 

        Read(line, len, move);
        return true;
    }

    bool GetLine(std::string* line) const
    {
        const char* pos = FindCRLF();
        if(pos == nullptr)
            return false;

        int len = 0;
        if(pos >= ReadPos())
            len = pos-ReadPos()+1;
        else
            len = RearReadAbleSize() + pos - BeginPos() + 1; 

        Read(line, len);
        return true;
    }

    void Write(const void* data, uint64_t len)
    {
        if(len == 0) return;
        EnsureWriteSpace(len);
        const char* d = (const char*) data;
        char* wp = WritePos();
        // write_index后的空闲空间足够写入，则全部在其中写入
        // 否则在写完write_index后的空闲空间后，继续在write_index前的空闲空间写入
        if(len <= RearFreeSize())
        {
            std::copy(d, d+len, wp);
        }
        else
        {
            std::copy(d, d+RearFreeSize(), wp);
            std::copy(d+RearFreeSize(), d+len, BeginPos());
        }
        MoveWriteIndex(len);
    }

    void Write(const std::string& data) 
    { 
        Write(data.c_str(), data.size()); 
    }

    void Write(Buffer& data, uint64_t len = -1, Move move = ReadOnly)
    { 
        if(len == -1) len = data.ReadAbleSize();
        Write(data.ReadPos(), len); 
        if(move == ReadAndMove)
            data.MoveReadIndex(len);
    } 

    void Clear() { _read_index = _write_index = 0; }

private:
    std::vector<char> _buffer;
    uint64_t _read_index  = 0;
    uint64_t _write_index = 0;
};

std::ostream& operator<<(std::ostream& _cout, const Buffer& buf)
{
    std::string buf_content;
    buf.Read(&buf_content);
    std::cout<<buf_content<<std::endl;
    return _cout;
}